/**
 * exponencial.h
 * Autores: Danilo S. Carvalho	- 107390588
 * 			Taísa L. Martins	- 107362828 
 *
 */

#ifndef exponencial_h
#define exponencial_h

#ifdef _WIN32
# include <windows.h>
# ifndef uint32_t 
#  define uint32_t unsigned long 
# endif
#else
# include <sys/time.h>
#endif

#define DSFMT_MEXP 19937 
#include "dSFMT/dSFMT.h"

#include <cstdlib>
#include <cmath>
#include <ctime>

namespace SimuladorAD {
    /**
     * \brief Classe responsável pela geração de números aleatórios. \n
     * Faz uso do SIMD-oriented Fast Mersenne Twister (SFMT), um algoritmo otimizado de geração de números
     * pseudo-aleatórios. No SFMT o período da sequência pseudo-aleatória é um múltiplo de 2^MEXT - 1, onde
     * MEXT é chamado "expoente de Mersene".
     * Para este simulador é usado o valor padrão MEXT = 19937, produzindo um período grande o suficiente para
     * os testes.
     */
    class Rng {
        dsfmt_t m_estado;  	/**< Estado do gerador. */
        unsigned long m_semente;	/**< Semente inicial do gerador. Deve ser guardada para garantir uniformidade na simulação. */

        public:
        /** Construtores do gerador. */
        /** Construtor padrão */
        Rng();

        /** Inicia o gerador com uma semente inicial. Um novo estado de gerador será inicializado. */
        Rng(unsigned long semente);

        /** Inicia o gerador com uma semente inicial e um estado pré-existente. 
         *
         * \param semente Semente inicial do gerador.
         * \param estado Estado anterior do gerador.
         * */
        Rng(unsigned long semente, dsfmt_t estado);

        /** Gera uma semente inicial */
        static unsigned long gerar_semente() ;

        /** Retorna o estado atual do gerador. */
        dsfmt_t estado() const;
        
        /** Retorna a semente inicial do gerador. */
        double semente() const;

        /** Retorna um número aleatório distribuido uniformemente entre 0 e 1, usando o SFMT. */
        double gerar();
        
    };
    
    /**
     * \brief Classe para representar a distribuição exponencial.
     */
    class Exponencial {
        double m_taxa;    			/**< Taxa da exponencial  (lambda). */
        Rng *m_gerador;   			/**< Gerador de números aleatórios que será usado na distribuição. */

        /** 
         * Retorna uma amostra da dist. Exponencial usando uma amostra u0 da dist. Uniforme 
         * através da função inversa da PDF.
         *
         * \param u0 Amostra da distribuição uniforme.
         */
        double inversa_cumulativa(double u0);

        bool m_deterministico;   	/**< Se a distribuição vai funcionar determinisicamente, ou seja, se será ou não uma V.A. */

        public:
       
        /** 
         * Construtor da distribuição. 
         * 
         * \param taxa Determina a taxa da distribuição.
         */
        Exponencial(double taxa);
        
        /** 
         * Construtor da distribuição. 
         * 
         * \param taxa Determina a taxa da distribuição.
         * \param semente Semente inicial do gerador de números aleatórios.
         * \param deterministico Se a instância se comportará como uma V.A. ou não.
         */
        Exponencial(double taxa, unsigned long semente, bool deterministico);
        
        /** 
         * Construtor da distribuição. 
         * 
         * \param taxa Determina a taxa da distribuição.
         * \param deterministico Se a instância se comportará como uma V.A. ou não.
         * \param gerador O gerador de números aleatórios usado para gerar as amostras.
         */
        Exponencial(double taxa, bool deterministico, Rng *gerador);
        
        /** 
         * Destrutor da distribuição. 
         */
        ~Exponencial();

        /** Retorna a semente inicial do gerador de números aleatórios interno. */
        double semente();

        /** Ativa/desativa o estado determinístico da distribuição. 
         *
         * \param ativado Se o modo determinístico será ativado ou não (true|false) respectivamente.
         * */
        void deterministico(bool ativado);
        
        /** Retorna o tempo para a ocorrência do próximo evento. */
        double gera_amostra();
    };
}

#endif //exponencial.h
